package cn.edu.sbs.talent.shiro.service.impl;

import cn.edu.sbs.talent.common.service.impl.BaseService;
import cn.edu.sbs.talent.shiro.entity.Permission;
import cn.edu.sbs.talent.shiro.entity.Role;
import cn.edu.sbs.talent.shiro.mapper.PermissionMapper;
import cn.edu.sbs.talent.shiro.mapper.RoleMapper;
import cn.edu.sbs.talent.shiro.service.IPermissionService;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 权限 业务逻辑实现类
 *
 * @author ctidy
 * @since 2021/1/7
 */
@Service
@Transactional
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class PermissionService extends BaseService<Permission> implements IPermissionService {

    private final PermissionMapper permissionMapper;
    private final RoleMapper roleMapper;

    @Override
    public List<Permission> listByAccount(long id) {
        return permissionMapper.selectBatchByAccountId(id);
    }

    @Override
    public List<Permission> listByAccount(String username) {
        Assert.hasLength(username, "用户名不能为空");
        return permissionMapper.selectBatchByAccountUsername(username);
    }

    @Override
    public List<Permission> listByRole(long id) {
        return permissionMapper.selectBatchByRoleId(id);
    }

    @Override
    public List<Permission> listByRole(String name) {
        Assert.hasLength(name, "角色名不能为空");
        return permissionMapper.selectBatchByRoleName(name);
    }

    @Override
    public boolean needFilter(String requestAPI) {
        Assert.hasLength(requestAPI, "请求接口不能为空");
        List<Permission> permissions = permissionMapper.selectList(null);
        for (Permission permission : permissions) {
            if (requestAPI.startsWith(permission.getUrl())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean canAccessByAccount(String requestAPI, long id) {
        Assert.hasLength(requestAPI, "请求接口不能为空");
        for (String url : listAuthorizedUrlByAccount(id)) {
            if (requestAPI.startsWith(url)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean canAccessByAccount(String requestAPI, String username) {
        Assert.hasLength(requestAPI, "请求接口不能为空");
        Assert.hasLength(username, "用户名不能为空");
        for (String url : listAuthorizedUrlByAccount(username)) {
            if (requestAPI.startsWith(url)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Set<String> listAuthorizedUrlByAccount(long id) {
        List<Permission> permissions = listByAccount(id);
        return permissions.stream().map(Permission::getUrl).collect(Collectors.toSet());
    }

    @Override
    public Set<String> listAuthorizedUrlByAccount(String username) {
        Assert.hasLength(username, "用户名不能为空");
        List<Permission> permissions = listByAccount(username);
        return permissions.stream().map(Permission::getUrl).collect(Collectors.toSet());
    }

    @Override
    public Map<String, String> loadFilterChainDefinitionMap() {
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
        // URL 过滤器链
        //   anon  : 允许匿名 (全员) 访问
        //   authc : 通过认证后方可访问

        // 错误信息
        filterChainDefinitionMap.put("/api/error/**","anon");

        // Swagger2
        filterChainDefinitionMap.put("/swagger-ui.html","anon");
        filterChainDefinitionMap.put("/swagger/**","anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**","anon");
        filterChainDefinitionMap.put("/v2/**","anon");
        filterChainDefinitionMap.put("/static/**", "anon");

        // 登录
        filterChainDefinitionMap.put("/api/auth/login","anon");
        // 登出
        filterChainDefinitionMap.put("/api/auth/logout","anon");
        // 冲突下线
        filterChainDefinitionMap.put("/api/auth/downline", "anon");

        List<Permission> permissions = permissionMapper.selectList(null);
        String template = "authc,%s,anyPerms[%s]";
        for (Permission permission : permissions) {
            List<Role> roles = roleMapper.selectBatchByPermissionId(permission.getId());
            StringJoiner rolesJoiner = new StringJoiner(",", "anyRoles[", "]");
            if (!ObjectUtils.isEmpty(roles)) {
                roles.forEach(role -> rolesJoiner.add(role.getName().toUpperCase()));
            }
            String authMethod = String.format(template, rolesJoiner.toString(), permission.getName());
            filterChainDefinitionMap.put(permission.getUrl(), authMethod);
        }
        filterChainDefinitionMap.put("/**", "authc");

        return filterChainDefinitionMap;
    }

    @Override
    public void refreshPermissions(ShiroFilterFactoryBean shiroFilterFactoryBean, long roleId, boolean isRemoveSession) throws Exception {
        synchronized (this) {
            AbstractShiroFilter shiroFilter;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
            } catch (Exception e) {
                throw new Exception("getting ShiroFilter from ShiroFilterFactoryBean error!");
            }
            Assert.notNull(shiroFilter, "get null ShrioFilter");
            PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
            DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();

            // 清空拦截管理器中的缓存
            manager.getFilterChains().clear();
            // 清空拦截工厂中的缓存
            shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
            // 动态查询数据库中所有权限
            shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitionMap());
            // 重新构建生成拦截
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                manager.createChain(entry.getKey(), entry.getValue());
            }

            // 动态更新该角色相关联的用户shiro权限
            refreshPermissionByRole(roleId,isRemoveSession);
        }
    }

    @Override
    public void refreshPermissionByRole(long roleId, boolean isRemoveSession) {

    }
}
